package com.flute.framework.index;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.FSDirectory;

import com.flute.framework.FluteMonitor;
import com.flute.framework.LuceneDocumentBuilder;
import com.flute.framework.StandardDate;
import com.flute.framework.data.IIndexData;
import com.flute.framework.exception.FatalIndexException;
import com.flute.framework.exception.IndexException;
import com.flute.framework.filesystem.FileDataBlock;
import com.flute.framework.index.analysis.SpliterAnalyzer;
import com.flute.framework.monitor.DataRecordLogger;
import com.flute.framework.monitor.EventLogger;
import com.flute.framework.monitor.MonitedValue;
import com.flute.framework.util.ftp.LocalFile;
import com.flute.tools.data.DataIterator;
import com.flute.tools.data.DataRecord;
import com.flute.tools.util.ExceptionUtil;

public class LuceneIndexer implements IIndexer {
	private String indexerName;
	private IIndexData data;
	private IndexFolder folder;
	private IDocumentBuilder builder;
	private boolean hasFatalExcepton;

	public LuceneIndexer() {
	}

	public LuceneIndexer(String location) {
		this.folder = new IndexFolder(location);
	}

	public void setBuilder(IDocumentBuilder builder) {
		this.builder = builder;
	}

	public String getLocation() {
		return folder.getLocation();
	}

	public void setLocation(String location) {
		this.folder = new IndexFolder(location);
	}

	@Override
	public List<StandardDate> getBackupDates() {
		return folder.getBackupDates();
	}

	@Override
	public List<StandardDate> getDataDates() {
		try {
			return data.getAllDates();
		} catch (IndexException e) {
			error("获取所有记录的时间失败.\n" + ExceptionUtil.getExceptionContent(e));
		}

		return new ArrayList<StandardDate>();
	}

	@Override
	public void rebuildFrom(StandardDate date) {
		long id = EventLogger.startNewEvent(FluteMonitor.getIndexerKey());

		DataIterator<DataRecord> it = null;
		try {
			if (date == null) {
				info("开始重建索引...");
				it = data.getCurrentDataRecords(Boolean.FALSE);
				trancationIndex(it, Boolean.FALSE);
			} else {
				info("从时间点'" + date.toNormalDateString() + "'开始重建索引...");
				it = data.getDataRecords(date);
				trancationIndex(it, Boolean.FALSE);
			}
		} catch (IndexException e) {
			error("获取重建索引所需记录失败.\n" + ExceptionUtil.getExceptionContent(e));
		} finally {
			if (it != null) {
				it.close();
			}
		}

		info("重建索引结束.");
		EventLogger.endEvent(id);
	}

	@Override
	public void setIndexData(IIndexData indexData) {
		this.data = indexData;
	}

	@Override
	public void update() {
		long eventId = EventLogger.startNewEvent(FluteMonitor.getIndexerKey());
		info("开始更新索引...");
		DataIterator<DataRecord> it = null;
		try {
			it = data.getCurrentDataRecords(Boolean.TRUE);
			if (it.hasNext()) {
				trancationIndex(it, Boolean.TRUE);
			} else {
				info("没有需要更新数据.");
			}
		} catch (Exception e) {
			error("建索引时出错, " + ExceptionUtil.getExceptionContent(e));
		} finally {
			if (it != null) {
				it.close();
			}
		}
		info("更新索引结束.");
		info("log id size:" + EventLogger.logIdInfo());
		EventLogger.endEvent(eventId);
	}

	private void trancationIndex(DataIterator<DataRecord> records,
			boolean update) {
		if (hasFatalExcepton) {
			error("索引更新程序发生了致命的异常，取消一切更新。");
			return;
		}
		try {
			folder.startIndex(update);
			data.openTransaction();
			index(records);
			folder.endIndex();
			data.commit();
		} catch (IndexException e) {
			error("建索引时出错，此次更新操作退出.\n" + ExceptionUtil.getExceptionContent(e));
		} catch (FatalIndexException e) {
			error("建索引时发生致命异常，取消以后任何更新.\n"
					+ ExceptionUtil.getExceptionContent(e));
			hasFatalExcepton = true;
		}
	}

	private void index(DataIterator<DataRecord> records) throws IndexException {
		IndexWriter writer = null;
		try {
			if (new FileDataBlock(folder.getRunningFolder(), new LocalFile())
					.isEmpty()) {
				writer = new IndexWriter(FSDirectory.open(new File(folder
						.getRunningFolder())), new SpliterAnalyzer(),
						Boolean.TRUE, IndexWriter.MaxFieldLength.LIMITED);
			} else {
				writer = new IndexWriter(FSDirectory.open(new File(folder
						.getRunningFolder())), new SpliterAnalyzer(),
						Boolean.FALSE, IndexWriter.MaxFieldLength.LIMITED);
			}
			writer.setMergeFactor(10000);
			writer.setMaxBufferedDocs(10000);

			processRecord(writer, records);
			info("optimize...");
			writer.optimize();
			// writer.commit();
		} catch (Exception e) {
			throw new IndexException(e);
		} finally {
			if (writer != null) {
				try {
					info("writer closing...");
					writer.close();
					info("writer closed");
				} catch (CorruptIndexException e) {
					e.printStackTrace();
					error(ExceptionUtil.getExceptionContent(e));
				} catch (IOException e) {
					e.printStackTrace();
					error(ExceptionUtil.getExceptionContent(e));
				}
			}
		}
	}

	private void processRecord(IndexWriter writer,
			DataIterator<DataRecord> records) {
		int i = 0;
		while (records.hasNext()) {
			DataRecord record = records.next();
			try {
				switch (record.getDataAction()) {
				case Add:
					removeRecord(writer, record);
					addRecord(writer, record);
					break;
				case Delete:
					removeRecord(writer, record);
					break;
				case Update:
					updateRecord(writer, record);
					break;
				default:
					removeRecord(writer, record);
					addRecord(writer, record);
					break;
				}
				dataInfo(record);
			} catch (CorruptIndexException e) {
				error("处理记录'" + record.toString() + "'失败.\n"
						+ ExceptionUtil.getExceptionContent(e));
				dataError(record);
			} catch (IOException e) {
				error("处理记录'" + record.toString() + "'失败.\n"
						+ ExceptionUtil.getExceptionContent(e));
				dataError(record);
			}
			System.out.println(record.getDataAction() + ":" + i++);
		}

		records.close();
		DataRecordLogger.getLogger(
				EventLogger.getCurrentEventId(FluteMonitor.getIndexerKey()))
				.endRecord();
	}

	private void addRecord(IndexWriter writer, DataRecord record)
			throws CorruptIndexException, IOException {
		Document doc = (Document) builder.buildDocument(record);
		writer.addDocument(doc);
	}

	private void removeRecord(IndexWriter writer, DataRecord record)
			throws CorruptIndexException, IOException {
		writer.deleteDocuments(new Term(LuceneDocumentBuilder.DOCUMENT_ID,
				record.getFieldValue(LuceneDocumentBuilder.DOCUMENT_ID)));
	}

	private void updateRecord(IndexWriter writer, DataRecord record)
			throws CorruptIndexException, IOException {
		removeRecord(writer, record);
		addRecord(writer, record);
	}

	private void info(String message) {
		EventLogger.info(new MonitedValue(message), EventLogger
				.getCurrentEventId(FluteMonitor.getIndexerKey()));
	}

	private void error(String message) {
		EventLogger.error(new MonitedValue(message), EventLogger
				.getCurrentEventId(FluteMonitor.getIndexerKey()));
	}

	private void dataInfo(DataRecord record) {
		DataRecordLogger.getLogger(

		EventLogger.getCurrentEventId(FluteMonitor.getIndexerKey())).recordInfo(
				record);
	}

	private void dataError(DataRecord record) {
		DataRecordLogger.getLogger(

		EventLogger.getCurrentEventId(FluteMonitor.getIndexerKey()))
				.recordError(record);
	}

	public String getIndexerName() {
		return indexerName;
	}

	public void setIndexerName(String indexerName) {
		this.indexerName = indexerName;
	}

	public IDocumentBuilder getBuilder() {
		return builder;
	}

	public IndexFolder getFolder() {
		return folder;
	}

	public void setFolder(IndexFolder folder) {
		this.folder = folder;
	}
}
